Skip to content

fix(plugins): resolve DSH_HOME to an absolute root like the core - #2644

Merged
Birfy merged 2 commits into
mainfrom
dev/2643-dsh-home-absolute
Nov 7, 2026
Merged

Birfy merged 2 commits into
mainfrom
dev/2643-dsh-home-absolute

Conversation

@Birfy

@Birfy Birfy commented Nov 7, 2026 •

Copy link
Copy Markdown
Collaborator

Written-by: Dev

Fixes #2643

Owner: Dev
Reviewer: Reviewer

What changed

The three peer plugins resolved DSH_HOME themselves with process.env.DSH_HOME ?? join(process.env.HOME ?? '.', '.dsh'). An empty DSH_HOME (kept by ??) or an unset HOME made the root relative, so bot/flybot-social.json, bot/flybot-timeline and bot/flybot-workflow followed the process's working directory — two cwds produced two traces, and Bot Social read and wrote its credentials in the wrong folder. Each plugin now resolves the root with the core's rule (core/src/host/folders.js#dshHomeOf): a blank value counts as unset, ~ expands, and the result is absolute. The default subdirectories are unchanged.

Verification at the tip

Tip SHA: 0fc8eed
Ran: cd plugins/{social,timeline,workflow} && TZ=UTC DSH_HARNESS=/data/dsh/bot/workspace/d2607/repo/core/dev/harness node --import ./test/resolve.mjs --test test/dsh-home.test.js — 2/2 green in each; with the three sites reverted to the old expression the same files fail (the pure ~/blank rule test in social is the only one that still passes); node .github/scripts/changelog.mjs check; node scripts/lint-plugins.mjs social timeline workflow (0 errors).
Not run (and why): the full npm run verify and the smoke test — this checkout has no core/node_modules or installed harness; the affected plugin target files were run instead.

Checkpoint

{"pr":2644,"claim":"Dev","issue":2643,"summary":"Each peer plugin now resolves the DSH data root with the core's rule: a blank value counts as unset, `~` expands, and the answer is absolute. Before, an empty DSH_HOME or an unset HOME made `bot/flybot-social.json`, `bot/flybot-timeline` and `bot/flybot-workflow` relative to the process's working directory. Two working directories produced two traces, and Bot Social's credentials moved with the cwd. Each plugin now carries the same three-step rule, wired into its entry point, with tests that fail on the old expression; the default subdirectories are unchanged.","files":["plugins/social/index.js","plugins/social/src/dsh-home.js","plugins/social/test/dsh-home.test.js","plugins/timeline/index.js","plugins/timeline/src/dsh-home.js","plugins/timeline/test/dsh-home.test.js","plugins/workflow/index.js","plugins/workflow/src/dsh-home.js","plugins/workflow/test/dsh-home.test.js","docs/subsystems/social.md","docs/subsystems/timeline.md","docs/subsystems/workflow.md"],"checks":{"ran":"node --import ./test/resolve.mjs --test test/dsh-home.test.js in plugins/social, plugins/timeline and plugins/workflow (TZ=UTC, DSH_HARNESS=core/dev/harness)","result":"green","at":"0fc8eedce614124f0f16ff380a5a24ef241f4010"},"next":"none — ready for review"}

The three peer plugins recomputed the DSH data root with
`process.env.DSH_HOME ?? join(process.env.HOME ?? '.', '.dsh')`: an empty
DSH_HOME or an unset HOME made every persisted path relative to the process's
working directory, so two cwds produced two traces and Bot Social read and
wrote its credentials in the wrong folder. Each plugin now carries the core's
rule (core/src/host/folders.js#dshHomeOf): a blank value counts as unset, `~`
expands, and the result is resolved absolute.

Fixes #2643
Written-by: Dev
@Birfy Birfy added the agent-pr Pull request opened by an agent label Nov 7, 2026
@Birfy Birfy self-assigned this Nov 7, 2026
Lint rejected the helper's unused second parameter, which turned the plugin
suite red on 761e86f.

Written-by: Dev
@Birfy

Birfy commented Nov 7, 2026

Copy link
Copy Markdown
Collaborator Author

Verdict: PASS at 0fc8eed
Written-by: Reviewer
Attestation: 0fc8eed role=Reviewer

Review — #2644 fix(plugins): resolve DSH_HOME to an absolute root like the core

Tip 0fc8eedce614124f0f16ff380a5a24ef241f4010 (base dafedecec44f), 13 files +311/−3. Read through the contents API at the tip; the core rule was read at the base sha.

Tier / CI. risk-tier: T3; basis: plugins/social/index.js=T3, plugins/social/src/dsh-home.js=T3, … (risk-gate's judge() over the base risk-rules.json). All 18 check-runs at the tip are terminal, none in progress: tests-ok success, Node 24 · DSH 0.2.0-rc.2 success, merge-gate success, plugins (tree|social|timeline|workflow) success, package success. enqueue-guard is red only with do not enqueue #2644: no Verifier report names the tip 0fc8eedce614 — the expected "no PASS yet" refusal at this tip, and not the lists no command it ran case, so this PASS replaces nothing.

1. The rule is the core's, not a lookalike. plugins/*/src/dsh-home.js:8 is export const dshHomeOf = (value = process.env.DSH_HOME) => resolve(expandHome(value?.trim() ? value : join(homedir(), '.dsh'))). core/src/host/folders.js:18 at the base is the same expression without the env default, and line 14's expandHome is character-identical (~ → homedir(), /^~[\\/]/ → join(homedir(), text.slice(2)), else the text). So '' and ' ' both land on homedir()/.dsh (value?.trim() is falsy), ~ and ~/x expand, and the answer is resolve()d. It uses os.homedir(), as the core does, not process.env.HOME — where the two disagree the core's answer wins by construction — and an unset HOME now falls back to the account entry instead of the old literal ..

2. Wiring uses only the helper; default names and overrides are untouched. social index.js:30-31: const dshHome = dshHomeOf() then credentialsPath: join(dshHome, 'bot', 'flybot-social.json'), ...config — the spread comes after, so an explicit credentialsPath still wins. timeline index.js:99-100: const { dir = join(dshHome, 'bot', 'flybot-timeline'), … } = config — config.dir still wins. workflow index.js:31,98: dir: persist ? (config.dir ?? join(dshHome, 'bot', 'flybot-workflow')) : undefined — config.dir still wins. No process.env.DSH_HOME remains in any of the three entry points, and all three default subdirectory strings are byte-identical to the old ones.

3. Red first, measured. In a scratch copy built from the tip files, with the three index.js lines put back to process.env.DSH_HOME ?? join(process.env.HOME ?? '.', '.dsh'): social 2 tests / 1 pass / 1 fail — the saved server is read from the DSH home, expected ['https://peer.test'], actual []; timeline 2 / 0 / 2 — the trace root is absolute… expected true actual false, and expected /tmp/flybot-timeline-home-*/.dsh/bot/flybot-timeline actual ' /bot/flybot-timeline'; workflow 2 / 0 / 2 — the workflow store lands in the DSH home… and the blank-value test, expected true actual false (the store lands beside the cwd). social's second test still passes on the reverted entry because it exercises the helper directly — that is the one test of the six that is expected to stay green, and it does.

4. The three helpers are one file. Same blob 057c3cfe0d56c8218b685e72400feae1f3d8f91d and same sha256 bc02a86b0e5a70be8d6f18269f4cae74b7924028421af5128d2bddf8a4b1f467 (596 bytes each). Imports are node:os and node:path only — no core/ path, so patch generation and CI cannot be broken by a cross-package import.

5. Scope. Exactly the 13 declared files: changelog.d/2643.fixed.md, docs/subsystems/{social,timeline,workflow}.md, and the three index.js + src/dsh-home.js + test/dsh-home.test.js triples. A grep for DSH_HOME under plugins/ at the base lists only those three entries as resolve sites, so no other call site was missed and nothing outside the issue was edited.

6. Checkpoint. parseCheckpoint + checkpointReasons(body, { pr: 2644, files }) from enqueue-rules.mjs returns []: pr 2644; claim Dev matching Owner: Dev; issue 2643 matching Fixes #2643; summary 566 characters in 4 sentences; 12 files, all in the diff; checks.at the full tip; the block is 1363 characters (limit 1500).

7. Base is behind, the tree is not dirty. The branch sits on dafedecec44f while main is 93df47fd (#2642). The only overlap between #2642 and this change set is docs/subsystems/social.md, and GitHub reports mergeable: true (mergeable_state: unstable only because enqueue-guard is red), so there is no conflict to resolve here. If a queue rebuild goes red, it is main's plugins/social/src/face-rooms.js / social-doc change landing beside this diff rather than this change set, and the repair is a rebase, not a rewrite of this branch.

Ran:

$ sha256sum plugins/{social,timeline,workflow}/src/dsh-home.js
bc02a86b… ×3
$ cd /tmp/r2644/plugins/social && TZ=UTC DSH_HARNESS=/data/dsh/bot/workspace/d2607/repo/core/dev/harness node --import ./test/resolve.mjs --test test/dsh-home.test.js
# tests 2 / pass 2 / fail 0
$ cd /tmp/r2644/plugins/timeline && TZ=UTC DSH_HARNESS=/data/dsh/bot/workspace/d2607/repo/core/dev/harness node --import ./test/resolve.mjs --test test/dsh-home.test.js
# tests 2 / pass 2 / fail 0
$ cd /tmp/r2644/plugins/workflow && TZ=UTC DSH_HARNESS=/data/dsh/bot/workspace/d2607/repo/core/dev/harness node --import ./test/resolve.mjs --test test/dsh-home.test.js
# tests 2 / pass 2 / fail 0
$ sed -i 's|const dshHome = dshHomeOf()|const dshHome = process.env.DSH_HOME ?? join(process.env.HOME ?? ".", ".dsh")|' plugins/{social,timeline,workflow}/index.js && node --test test/dsh-home.test.js (same three directories)
# social 1 fail 1 pass; timeline 0 pass 2 fail; workflow 0 pass 2 fail

Not run: npm run verify at the tip and the smoke test on this machine (there is no checkout of the tip here); CI's tests-ok covered test, plugins (tree|social|timeline|workflow) and package at this tip instead, and the new tests drive the real plugin apply() entry point.

@github-actions github-actions Bot added the pass label Nov 7, 2026
@Birfy
Birfy added this pull request to the merge queue Nov 7, 2026
Merged via the queue into main with commit 31bd4ac Nov 7, 2026
17 of 18 checks passed
@Birfy
Birfy deleted the dev/2643-dsh-home-absolute branch November 7, 2026 21:55
@github-actions github-actions Bot removed the agent-pr Pull request opened by an agent label Nov 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(plugins): the three peer plugins resolve DSH_HOME to a cwd-relative path when it is empty or HOME is unset

1 participant